-module(sender).
-export([start/7]).
-define(SENDOFFSET, 19).
-define(LISTENTIME, 5).
-define(WAKEUPDELAY, 15).

start(PipePID,SyncPID, SlotPID, Port, IP, MultIP, StationsKlasse) -> %evt. fehlende Paramter auch im tdmaclient:start nachtragen
	Socket = get_socket(sender, IP),
  pre_start(Socket, Port, MultIP, PipePID, SyncPID, StationsKlasse, SlotPID).

pre_start(Socket, Port, MultIP, PipePID, SyncPID, StationsKlasse, SlotPID) ->
  timer:sleep(?LISTENTIME*1000),
  start_sending(Socket, Port, MultIP, PipePID, SyncPID, StationsKlasse, SlotPID, wait_until_next_frame(SyncPID, SlotPID)).
  
wait_until_next_frame(SyncPID, SlotPID) ->
  Now = synchro:get_time(SyncPID),
  NextFrameBegin = round(Now / 1000 + 0.5)*1000,
  timer:sleep(NextFrameBegin - Now -?WAKEUPDELAY),
  SlotPID ! getnextfreeslot,
  receive 
    {nextfreeslot, NewSlotNr} -> NewSlotNr
  end.


start_sending(Socket, Port, MultIP, PipePID, SyncPID, StationsKlasse,SlotPID, Slot) ->%%Multicast IP?
	TimeStamp = synchro:get_time(SyncPID),
	FrameNr = round(TimeStamp/1000-0.5)+1,
	TimeToSend = (FrameNr * 1000) + (Slot * 40),
	receive 
		{collisiondetected, _} -> 
      start_sending(Socket, Port, MultIP, PipePID, SyncPID, StationsKlasse, SlotPID, wait_until_next_frame(SyncPID, SlotPID))
		after TimeToSend - TimeStamp + ?SENDOFFSET ->
			ok %Wenn bis dahin keine Kollisionsmeldung, dann einfach senden
	end,
	SyncPID ! {gettime, self()},
	PipePID ! {getmessage, self()},
	SlotPID ! getnextfreeslot,
	{TimeStampToSend, MessageToSend, SlotNr} = get_Time_Message_Slot(), %SlotNr = Slot im nächsten Frame
	BinMsg = list_to_binary(enforce_list_size(MessageToSend,24)),
	BinClass = list_to_binary(StationsKlasse),
	Data = <<BinMsg/bitstring,BinClass/bitstring, SlotNr:8/integer,TimeStampToSend:64/integer-big>>,
	SlotPID ! {sending, Slot},
	gen_udp:send(Socket, MultIP, Port, Data),
	start_sending(Socket, Port, MultIP, PipePID, SyncPID, StationsKlasse, SlotPID, SlotNr).

%%Ganz wichtig! Diese Funktion nur einmal aufrufen und Rückgabewert in einer Variablen speichern!
get_socket(sender,IP)-> %IP ist hier immer die eigene IP (Networkinterface)
    {ok,Socket} = gen_udp:open(0,[binary, {active, true},
    									{reuseaddr,true},
                                        {ip, IP},
                                        inet, {multicast_ttl, 1},
                                        {multicast_loop, true}, %<<- Fuck es lag an diesem Flag!
                                        {multicast_if, IP}]),
    Socket.
    

get_Time_Message_Slot() ->
	get_Time_Message_Slot({undefined, undefined, undefined}).
get_Time_Message_Slot({Time, Message, SlotNr}) when Time /= undefined andalso Message /= undefined andalso SlotNr /= undefined ->
	{Time, Message, SlotNr};
get_Time_Message_Slot({Time, Message, SlotNr}) -> 
	receive 
		{time, TimeStamp} -> get_Time_Message_Slot({TimeStamp, Message, SlotNr});
		{message, NewMessage} -> get_Time_Message_Slot({Time, NewMessage, SlotNr});
		{nextfreeslot, NewSlotNr} -> get_Time_Message_Slot({Time, Message, NewSlotNr})
	end.


%%Schneidet zu lange Werte ab und füllt zu kurze Listen mit 0-en Auf
enforce_list_size(List, Size) when length(List) == Size ->
	List;
enforce_list_size(List, Size) when length(List) > Size ->
	enforce_list_size(lists:nthtail(length(List)-Size, List), Size);
enforce_list_size(List, Size) ->
	enforce_list_size(lists:append(List,[32]), Size).


%get_Time_and_Message2() ->
%	receive
%		{time, TimeStamp2} ->
%			TimeStampToSend = TimeStamp2;
%		{message, Message} ->
%			MessageToSend = Message
%	end,
%	receive
%		{time, TimeStamp3} ->
%			TimeStampToSend = TimeStamp3;
%		{message, Message} ->
%			MessageToSend = Message
%	end,
%	{TimeStampToSend, MessageToSend}.

%% Usage
%%Socket=tools:get_socket(sender,Port,Ip),
%%gen_udp:controlling_process(Socket,self()),

%%gen_udp:send(Socket, Adress, Port, Packet)